const {Board} = require('board-game');
const extend = require('extend');
const Enumerable = require('linq-js');

const common = require('./common');
const words = require('./words');
const {game: gameConfig} = require('./config');


class UndercoverBoard extends Board {
    constructor(game) {
        super(game);

        this.stage = common.stages.confirm;
        this.tick = 0;

        this.random();

        this.describer = 0;
        this.redescribers = [];
        this.revotes = [];
        this.interval = setInterval(() => this.step(), common.fps);
    }

    random() {
        this.getRoles().forEach(role => { role.type = common.roles.none; role.word = ''; role.status = common.status.confirming });
        let keywords = Enumerable.rand(Enumerable.random(words), 2).toArray();
        this.keyword = keywords[0];
        this.undercover = keywords[1];
        this.whiteboardCount = this.settings.whiteboard && this.groups.length >= common.counts.whiteboard ? Math.floor(this.groups.length * common.percent.whiteboard / 100) : 0;
        this.accompliceCount = this.settings.accomplice && this.groups.length >= common.counts.accomplice ? Math.floor(this.groups.length * common.percent.accomplice / 100) : 0;
        this.undercoverCount = Math.floor(this.groups.length * common.percent.undercover / 100);
        this.helperCount = this.settings.helper && this.groups.length >= common.counts.helper ? Math.floor(this.groups.length * common.percent.helper / 100) : 0;
        this.normalCount = this.groups.length - this.whiteboardCount - this.undercoverCount - this.accompliceCount;
        console.log(`平民数量:${ this.normalCount },卧底数量:${ this.undercoverCount },白板数量:${ this.whiteboardCount },帮凶数量:${ this.accompliceCount }`);
        if (this.whiteboardCount > 0) this.getRoles().skip(Math.floor(this.groups.length / 2)).rand(this.whiteboardCount).forEach(role => role.setType(common.roles.whiteboard, ''));
        if (this.accompliceCount > 0) {
            this.getRoles().take(Math.floor(this.groups.length / 2)).where(role => role.type === common.roles.none).rand(1).forEach(role => role.setType(common.roles.undercover, this.undercover));
            if (this.undercoverCount > 1) {
                this.getRoles().where(role => role.type === common.roles.none).rand(this.undercoverCount - 1).forEach(role => role.setType(common.roles.undercover, this.undercover));
            }
            this.getRoles().skipWhile(role => role.type !== common.roles.undercover).where(role => role.type === common.roles.none).rand(this.accompliceCount).forEach(role => {
                role.setType(common.roles.accomplice, '');
                role.help = this.getRoles().indexOf(this.getRoles().takeWhile(r => r !== role).where(r => role.type === common.roles.undercover).rand(1).first());
            });
        } else {
            this.getRoles().where(role => role.type === common.roles.none).rand(this.undercoverCount).forEach(role => role.setType(common.roles.undercover, this.undercover));
        }
        this.getRoles().where(role => role.type === common.roles.none).forEach(role => role.setType(common.roles.normal, this.keyword));
        this.getRoles().forEach(role => console.log(`${ role.group }号玩家${ role.player.name }的身份为:${ common.roleLabel[role.type] }`))
    }

    step() {
        this.tick++;
        switch (this.stage) {
            case common.stages.confirm:
                if (this.tick >= common.fs.confirm) {
                    this.getRoles().where(role => role.status === common.status.confirming).forEach(role => role.doConfirm());
                }
                if (this.tick * 5 > common.fs.confirm || this.getRoles().count(role => role.type !== common.roles.whiteboard && role.type !== common.roles.accomplice && role.status !== common.status.confirming) * 2 >= this.getRoles().count(role => role.type !== common.roles.whiteboard && role.type !== common.roles.accomplice)) {
                    this.getRoles().where(role => (role.type === common.roles.whiteboard || role.type === common.roles.accomplice) && role.status === common.status.confirming).take(1).forEach(role => role.doConfirm());
                }
                if (this.getRoles().all(role => role.status !== common.status.confirming)) {
                    if (this.getRoles().count(role => role.status === common.status.confirmed) / this.groups.length > common.percent.confirmed / 100) {
                        this.nextStage(common.stages.starting);
                    } else {
                        this.nextStage(common.stages.reconfirm);
                    }
                }
                break;
            case common.stages.reconfirm:
                if (this.tick >= common.fs.reconfirm) {
                    this.random();
                    this.nextStage(common.stages.confirm);
                }
                break;
            case common.stages.starting:
                if (this.tick >= common.fs.starting) {
                    this.nextStage(common.stages.describe);
                    this.describer = 0;
                }
                break;
            case common.stages.describe:
                if (this.tick >= common.fs.describe) {
                    this.nextDescriber();
                }
                break;
            case common.stages.redescribe:
                if (this.tick >= common.fs.redescribe) {
                    this.nextDescriber();
                }
                break;
            case common.stages.redescribing:
                if (this.tick >= common.fs.redescribing) {
                    this.nextStage(common.stages.redescribe);
                    this.describer = this.redescribers.shift();
                    this.revotes = [];
                }
                break;
            case common.stages.tip:
                if (this.tick >= common.fs.tip) {
                    this.nextStage(common.stages.describe);
                    this.describer = this.getRoles().first(role => !role.dead).group;
                }
                break;
            case common.stages.vote:
                if (this.tick >= common.fs.vote) {
                    this.confirmVote();
                    this.randomVote();
                }
                if (this.getRoles().where(role => !role.dead).all(role => role.vote !== false)) {
                    this.getRoles().forEach(role => {
                        role.votes = 0;
                    })
                    let maxIndices = this.getRoles().where(role => !role.dead).groupBy(role => role.vote).select(grouping => ({
                        key: grouping.key,
                        count: grouping.count()
                    })).orderByDescending(grouping => grouping.count).takeSame((grouping, other) => grouping.count === other.count).select(grouping => grouping.key).toArray();
                    if (maxIndices.length === 1) {
                        this.groups[maxIndices[0]].roles[0].dead = true;
                        if (!this.checkOver()) {
                            this.nextStage(common.stages.tip);
                        } else {
                            this.nextStage(common.stages.end);
                        }
                    } else {
                        if (this.settings.randomDead) {
                            let deadIndex = Enumerable.random(maxIndices);
                            this.groups[deadIndex].roles[0].dead = true;
                            if (!this.checkOver()) {
                                this.nextStage(common.stages.tip);
                            } else {
                                this.nextStage(common.stages.end);
                            }
                        } else {
                            //重新描述
                            this.redescribers = Enumerable.orderBy(maxIndices).toArray();
                            this.nextStage(common.stages.redescribing);
                        }
                    }
                } else {
                    this.getRoles().forEach(role => {
                        role.setVotes(this.getRoles().count(r => !r.dead && r.vote === role.group))
                    })
                }
                break;
            case common.stages.end:
                if (this.tick >= common.fs.end) {
                    this.game.over(this.winType);
                    clearInterval(this.interval);
                }
                break;
        }
    }

    confirmVote() {
        this.getRoles().where(role => role.vote === false && role.preVote !== false).forEach(role => role.doVote());
    }

    randomVote() {
        this.getRoles().where(role => role.vote === false).forEach(role => role.doVote(this.getRoles().where(r => r !== role && !r.dead).rand(1).first().group));
    }

    nextDescriber() {
        if (this.stage === common.stages.redescribe) {
            this.revotes.push(this.describe);
            if (this.redescribers.length) {
                this.describer = this.redescribers.shift();
                this.tick = 0;
            } else {
                this.getRoles().forEach(role => role.vote = false);
                this.nextStage(common.stages.vote);
            }
        } else {
            this.describer++;
            if (this.describer >= this.groups.length) {
                this.getRoles().forEach(role => role.vote = false);
                this.nextStage(common.stages.vote);
            } else if (this.groups[this.describer].roles[0].dead) {
                this.nextDescriber();
            } else {
                this.tick = 0;
            }
        }
    }

    nextStage(stage) {
        this.stage = stage;
        this.tick = 0;
        this.getRoles().forEach(role => role.clearVote());
    }

    doDescribeEnd(role) {
        if (this.describer === role.group) {
            this.nextDescriber();
        }
    }

    checkOver() {
        if (
            this.getRoles().where(role => role.type === common.roles.undercover || role.type === common.roles.accomplice).all(role => role.dead) && this.getRoles().any(role => role.type === common.roles.whiteboard && !role.dead)
            //卧底或帮凶全出局,白板未出局,白板获胜
        ) {
            //clearInterval(this.interval);
            this.winType = (common.roles.whiteboard);
            return true;
        } else if (
            this.getRoles().count(role => !role.dead) <= Math.ceil(this.groups.length * common.percent.win / 100) + 1 && this.getRoles().any(role => role.type === common.roles.undercover && !role.dead) ||
            //人数低于1/7时,卧底未出局,卧底或帮凶获胜
            this.getRoles().all(role => role.dead || role.type === common.roles.undercover || role.type === common.roles.accomplice)
            //仅剩余卧底或帮凶时,卧底或帮凶获胜
        ) {
            //clearInterval(this.interval);
            this.winType = (common.roles.undercover);
            return true;
        } else if (this.getRoles().where(role => role.type === common.roles.whiteboard || role.type === common.roles.undercover).all(role => role.dead)) {
            //白板和卧底都出局了,平民获胜
            //clearInterval(this.interval);
            this.winType = (common.roles.normal);
            return true;
        }
    }

    getRoles() {
        return Enumerable.from(this.groups).selectMany(group => group.roles);
    }

    get countdown() {
        return common.fs[this.stage] - this.tick;
    }

    get roleInfos() {
        return this.getRoles().select(role => role.data).toArray();
    }

    get data() {
        return extend(true, super.data, {
            stage: this.stage,
            describer: this.describer,
            whiteboardCount: this.whiteboardCount,
            undercoverCount: this.undercoverCount,
            normalCount: this.normalCount,
            helpCount: this.helperCount,
            tick: this.tick,
            countdown: this.countdown,
            roles: this.roleInfos,
            revotes: this.revotes,
            redescribers: this.redescribers,
            winType: this.winType,
        })
    }
}

module.exports = UndercoverBoard;
